
capture {
	program drop gmm_ols
}

program gmm_ols

	version 14.2
	
	syntax varlist [if] , at(name) factors(varlist) assets(namelist) assetclasses(namelist) return_prefix(name) [derivatives(varlist) weights(name) rfvars(varlist)]

	local nfactors: word count `factors'
	
	local nrf: word count `rfvars'

	local nassets: word count `assets'
	
	local nclasses: word count `assetclasses'
	
	local nreg = `nfactors'+`nrf'+1
	
	local nmom: word count `varlist'
	
	if `nmom' > `nassets' {
		local nderiv = `nreg' * `nassets' + `nassets' + `nfactors' + `nfactors' + `nclasses'
	}
	else {
		local nderiv = `nreg' * `nassets' 
	}
	
	local nd2: word count `derivatives'

	
	/*disp "nmom: `nmom' nassets: `nassets' nd: `nderiv' nd2: `nd2'"
	disp "derivs: `derivatives'"

	disp "assets: `assets'"
	disp "nreg: `nreg'"
	matrix list `at'*/
	
	if `nmom' > `nassets' {
		foreach fctr in `factors' {
			matrix temp`fctr' = J(1,1,0)
		}
		
		foreach cls in `assetclasses' {
			matrix tempcls_`cls' = J(1,1,0)
		}
	}
	
	if "`derivatives'" != "" {
		foreach deriv in `derivatives' {
			quietly replace `deriv' = 0 `if'
		}
	}
	
	forvalues i = 1/`nassets' {
		
	
		local mom : word `i' of `varlist'
		
		local asset : word `i' of `assets'
		
		tempvar xb
		
		//disp "asset: `asset'"
		
		matrix score double `xb' = `at' `if', equation(`asset')
	
		//disp "`return_prefix'`asset'"
	
		quietly replace `mom' = `return_prefix'`asset' - `xb' `if'
		quietly replace `mom' = . if `return_prefix'`asset' == .
		
		if `nmom' > `nassets' {
			local i2 = `i' + `nassets'
			local mom2 : word `i2' of `varlist'
		
			matrix temp`asset' = `at'[1,"means:`return_prefix'`asset'"] 
			*matrix list temp
			quietly replace `mom2' = `return_prefix'`asset' - temp`asset'[1,1] `if'
			quietly replace `mom2' = . if `return_prefix'`asset' == .
		
			//matrix list temp`asset'
			foreach fctr in `factors' {
				matrix temp`asset' = temp`asset' - `at'[1,"`asset':`fctr'"] *  `at'[1,"lambda:`fctr'"]
				//matrix list temp`asset'
			}
			
			
			foreach cls in `assetclasses' {
				if regexm("`asset'","`cls'") {
					matrix temp`asset' = temp`asset' - `at'[1,"lambda`cls':_cons"]
				}
			}
			//matrix list temp`asset'
			
			foreach fctr in `factors' {
				if "`weights'" != "" {
					matrix temp`fctr' = temp`fctr' + temp`asset' * `at'[1,"`asset':`fctr'"] * `weights'[1,"`asset'"]
				}
				else {
					matrix temp`fctr' = temp`fctr' + temp`asset' * `at'[1,"`asset':`fctr'"]
				}
				//matrix list temp`fctr' 
			}
			foreach cls in `assetclasses' {
				if regexm("`asset'","`cls'") {
					if "`weights'" != "" {
						matrix tempcls_`cls' = tempcls_`cls' + temp`asset' * `weights'[1,"`asset'"]
					} 
					else {
						matrix tempcls_`cls' = tempcls_`cls' + temp`asset'
					}
					//matrix list tempcls_`cls'
				}
			}
			
			//matrix drop temp`asset'
		}
		
		if "`derivatives'" != "" {
		
			
			
			forvalues j = 1/`nreg' {
				local w = (`i'-1)*`nderiv'+(`i'-1)*`nreg'+`j'
				//disp "w: `w'"
				local momd : word `w' of `derivatives'
				//disp "`momd'"
				
				/*if `j' == `nreg' {
					local fctr 1
				}
				else {
					if `j' <= `nfactors' {
						local fctr : word `j' of `factors'
					}
					else {
						local j2 = `j'-`nfactors'
						local fctr : word `j2' of `rfvars'
					}
				}*/
				
				local fctr : word `j' of `factors' `rfvars' 1
				
				//disp "fctr: `fctr'"
				
				quietly replace `momd' = -`fctr' `if'
				quietly replace `momd' = . if `return_prefix'`asset' == .
			}
			
			if `nmom' > `nassets' {
			
				//disp "asset `i': `asset'"
			
				local w = (`i'+`nassets'-1)*`nderiv'+`nassets'*`nreg'+`i'
				//disp "wderiv: `w'"
				local momd : word `w' of `derivatives'
				//disp "`momd'"
				
				quietly replace `momd' = -1 `if'
				quietly replace `momd' = . if `return_prefix'`asset' == .
			
				local nj = `nfactors' + `nclasses' 
				forvalues j = 1/`nj' {
				
					local eqn = `j'+2*`nassets'-1
					//disp "eqn: `eqn'"
					
					if `j' <= `nfactors' {
						local fctr : word `j' of `factors'
						//disp "fctr: `fctr'"
						local val `at'[1,"`asset':`fctr'"]
					}
					else {
						local cj = `j' - `nfactors'
						local cls: word `cj' of `assetclasses'
						//disp "cls: `cls'"
						local val = regexm("`asset'","`cls'")
					}
				
					//deriv for mean_asset
					local w3 = `eqn'*`nderiv'+`nassets'*`nreg'+`i'
					//disp "w3deriv: `w3'"
					local momd3 : word `w3' of `derivatives'
					//disp "`momd3'"
				
					if "`weights'" != "" {
						matrix temp3 = `val'* `weights'[1,"`asset'"]
					}
					else {
						matrix temp3 = `val'
					}
					//matrix list temp3
					quietly replace `momd3' = temp3[1,1] `if'
					
				
					forvalues j2 = 1/`nfactors' {
					
						local fctr2 : word `j2' of `factors'
						//disp "fctr2: `fctr2'"
						
						//disp "eqn: `eqn', i: `i' j2: `j2'"
						
						//deriv for beta_asset
						local w1 = `eqn'*`nderiv'+(`i'-1)*`nreg'+`j2'
						//disp "w1deriv: `w1'"
						local momd : word `w1' of `derivatives'
						//disp "`momd'"
				
						//deriv for lambda_asset
						local w2 = `eqn'*`nderiv'+`nassets'*`nreg'+`nassets'+`nfactors'+`j2'
						//disp "w2deriv: `w2'"
						local momd2 : word `w2' of `derivatives'
						//disp "`momd2'"
						
						if `j2' == `j' {
							matrix temp = temp`asset' - `at'[1,"lambda:`fctr2'"]*`val'
						}
						else {
							matrix temp = - `at'[1,"lambda:`fctr2'"]*`val'
						}
						matrix temp2 = -`at'[1,"`asset':`fctr2'"]*`val'
						
						//matrix list temp
						//matrix list temp2
						
						
						if "`weights'" != "" {
							matrix temp = temp[1,1] * `weights'[1,"`asset'"]
							matrix temp2 = temp2[1,1] * `weights'[1,"`asset'"]
							//matrix test = `weights'[1,"`asset'"]
							//matrix list test
						}
					
						quietly replace `momd' = temp[1,1] `if'
					
						quietly replace `momd2' = `momd2' + temp2[1,1] `if'
						
					}
					if "`weights'" != "" {
						matrix temp4 = -`val' * `weights'[1,"`asset'"]
					}
					else {
						matrix temp4 = -`val'
					}
					//deriv for lambda constants
					forvalues cj = 1/`nclasses' {
						local cls: word `cj' of `assetclasses'
						if regexm("`asset'","`cls'") {
							local wc_`cj' = `eqn'*`nderiv'+`nassets'*`nreg'+`nassets'+`nfactors'+`nfactors'+`cj'
							//disp "wc_`cj'deriv: `wc_`cj''"
							local momdc_`cj' : word `wc_`cj'' of `derivatives'
							//disp "`momdc_`cj''"
							
							//matrix list temp4
							//su `momdc_`cj''
							quietly replace `momdc_`cj'' = `momdc_`cj'' + temp4[1,1] `if'
							//su `momdc_`cj''
						}
					}
					
				}
				
			}
			
			
		}
			
	}
	

	if `nmom' > `nassets' {
		local i3 = 2 * `nassets'
		
		foreach fctr in `factors' {
			local i3 = `i3' + 1
			local mom : word `i3' of `varlist'
			
			quietly replace `mom' = temp`fctr'[1,1] `if'
		}
		
		foreach cls in `assetclasses' {
			local i3 = `i3' + 1
			local mom : word `i3' of `varlist'
			
			quietly replace `mom' = tempcls_`cls'[1,1] `if'
		}
		
		local nf = 0
		foreach fctr in `factors' {
			local i3 = `i3' + 1
			local nf = `nf' + 1
			local momf : word `i3' of `varlist'

			matrix temp`fctr' = `at'[1,"means:`fctr'"]

			quietly replace `momf' = `fctr' - temp`fctr'[1,1] `if'
			quietly replace `momf' = . if `fctr' == .
			
			if "`derivatives'" != "" {
			
				local i4 = `nderiv'*(`i3'-1)+`nassets'*`nreg'+`nassets'+`nf'
				local momd : word `i4' of `derivatives'
				quietly replace `momd' = -1 `if'
				quietly replace `momd' = . if `fctr' == .
			}
		}
		
	
	}
	
	
end
